/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lambkit.web;

import cn.hutool.core.util.StrUtil;
import com.jfinal.aop.Aop;
import com.jfinal.core.ActionException;
import com.jfinal.core.Controller;
import com.jfinal.core.NotAction;
import com.jfinal.kit.Kv;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import com.lambkit.db.mgr.*;
import com.lambkit.JFLambkit;
import com.lambkit.util.ServletRequestKit;
import com.lambkit.web.log.PageViewLogService;
import com.lambkit.web.render.MimeTypeRender;
import com.lambkit.util.SqlKit;

import javax.servlet.http.HttpServletRequest;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 控制器基类
 */
public abstract class LambkitController extends Controller {
	
	/**
     * <获取参数map>
     * 
     * @return 参数map
     * @throws Exception 
     */
    protected Kv getParameterMap() {
        Kv resultMap = Kv.create();
        Map<String, String[]> tempMap = getRequest().getParameterMap();
        Set<String> keys = tempMap.keySet();
        for (String key : keys) {
            //byte source [] = getRequest().getParameter(key).getBytes("iso8859-1");
            //String modelname = new String (source,"UTF-8");
            resultMap.set(key, getPara(key));
        }
        return resultMap;
    }
	
	/**
     * 获取当前网址
     *
     * @return
     */
	@NotAction
    public String getBaseUrl() {
        HttpServletRequest req = getRequest();
        int port = req.getServerPort();
        return port == 80
                ? String.format("%s://%s%s", req.getScheme(), req.getServerName(), req.getContextPath())
                : String.format("%s://%s%s%s", req.getScheme(), req.getServerName(), ":" + port, req.getContextPath());

    }

	/**
	 * 获取mgrdb配置
	 * @return
	 */
	@NotAction
	public MgrTable getBase(int type, boolean attr, String orderby) {
		//convert tbid type (int) to Long
		Long tbid=getParaToLong(0) == null ? getParaToLong("tag", null) : getParaToLong(0);
		if(tbid!=null) {
			return getTable(tbid, type, attr, orderby);
		}
		return null;
	}
	
	@NotAction
	public MgrTable getBase(boolean attr) {
		return getBase(MgrConstants.NONE, attr, null);
	}
	
	@NotAction
	public MgrTable getBase(int type, boolean attr) {
		return getBase(type, attr, null);
	}
	
	@NotAction
	public MgrTable getBase() {
		return getBase(MgrConstants.NONE, true, null);
	}
	
	@NotAction
	public MgrTable getBase(int type) {
		return getBase(type, true, null);
	}
	
	@NotAction
	public MgrTable getBase(String orderby) {
		return getBase(MgrConstants.NONE, true, orderby);
	}
	
	@NotAction
	public MgrTable getBase(int type, String orderby) {
		return getBase(type, true, orderby);
	}
	
	String mgrdbName = null;
	
	@NotAction
	public void setMgrdbName(String mgrdbName) {
		this.mgrdbName = mgrdbName;
	}
	
	protected MgrdbService getTableConfigService() {
		return JFLambkit.getApp().getBean(MgrdbService.class);
	}

	@NotAction
	public MgrTable getMgrTable(String tableName, boolean attr) {
		return getTable(tableName, MgrConstants.NONE, attr);
	}
	
	@NotAction
	public MgrTable getTableDefault(Object tbid, boolean attr) {
		return getTable(tbid, MgrConstants.NONE, attr);
	}
	
	@NotAction
	public MgrTable getTableEdit(Object tbid, boolean attr) {
		return getTable(tbid, MgrConstants.EDIT, attr);
	}
	
	@NotAction
	public MgrTable getTableView(Object tbid, boolean attr) {
		return getTable(tbid, MgrConstants.VIEW, attr);
	}
	
	@NotAction
	public MgrTable getTable(Object tbid, int type, boolean attr) {
		return getTable(tbid, type, attr, null);
	}
	
	@NotAction
	public MgrTable getTable(Object tbid, int type, boolean attr, String orderby) {
		MgrTable t = getAttr("mgrdb");
		if(t!=null && t.getId()==tbid) {
			return t;
		}
		else if(tbid!=null) {
			MgrTable tbc = getTableConfigService() !=null ? getTableConfigService().createTable(tbid, type, orderby) : null;
			if(attr) {
				if(tbc.getModel()!=null) {
					setAttr("tag", tbc.getModel().getId());
				}
				setAttr("mgrdb", tbc);
			}
			return tbc;
			/*
			TableConfig tbc = null;
			Object obj = CacheKit.get("ehCacheTableData", "tbconfig" + tbid);
			if(obj==null) {
				tbc = getTableConfigService() !=null ? getTableConfigService().createTable(tbid) : null;
				// 保存uset
				CacheKit.put("ehCacheTableData", "tbconfig" + tbid, tbc);
			} else {
				tbc = (TableConfig) obj;
			}
			return tbc;
			*/
		}
		return null;
	}
	
	@NotAction
	public MgrTable getTableDefault(String tbname, boolean attr) {
		return getTable(tbname, MgrConstants.NONE, attr);
	}
	
	@NotAction
	public MgrTable getTableEdit(String tbname, boolean attr) {
		return getTable(tbname, MgrConstants.EDIT, attr);
	}
	
	@NotAction
	public MgrTable getTableView(String tbname, boolean attr) {
		return getTable(tbname, MgrConstants.VIEW, attr);
	}
	
	@NotAction
	public MgrTable getTable(String tbname, int type, boolean attr) {
		return getTable(tbname, type, attr, null);
	}
	
	@NotAction
	public MgrTable getTable(String tbname, int type, boolean attr, String orderby) {
		MgrTable t = getAttr("mgrdb");
		if(t!=null && t.getName().equals(tbname)) {
			return t;
		}
		else if(StrUtil.isNotBlank(tbname)) {
			MgrTable tbc = getTableConfigService() !=null ? getTableConfigService().createTable(tbname, type, orderby) : null;
			if(attr) {
				if(tbc.getModel()!=null) {
					setAttr("tag", tbc.getModel().getId());
				}
				setAttr("mgrdb", tbc);
			}
			return tbc;
			/*
			TableConfig tbc = null;
			Object obj = CacheKit.get("ehCacheTableData", "tbconfig" + tbid);
			if(obj==null) {
				tbc = new TableConfig(tbid);
				// 保存uset
				CacheKit.put("ehCacheTableData", "tbconfig" + tbid, tbc);
			} else {
				tbc = (TableConfig) obj;
			}
			return tbc;
			*/
		}
		return null;
	}
	
	@NotAction
	public void keepTable(MgrTable tbc) {
		if(tbc==null) {
			return;
		}
		for(IField fld : tbc.getFieldList()) {
			String val = getPara(fld.getName());
			if(StrUtil.isNotBlank(val)) {
				fld.putAttr("val", val);
			}
		}
	}
	
	@NotAction
	public void keepTable(MgrTable tbc, Model model) {
		if (tbc == null) {
			return;
		}
		if(model==null) {
			return;
		}
		List fields = tbc.getFieldList();
		for (int i = 0; i < fields.size(); i++) {
			IField fld = (IField) fields.get(i);
			Object val = model.get(fld.getName());
			fld.putAttr("val", val);
		}
	}

	@NotAction
	public void setTableAttr(MgrTable tbc, Record model) {
		if (tbc == null) {
			return;
		}
		if(model==null) {
			return;
		}
		List fields = tbc.getFieldList();
		for (int i = 0; i < fields.size(); i++) {
			IField fld = (IField) fields.get(i);
			Object val = model.get(fld.getName());
			fld.putAttr("val", val);
		}
	}

	@NotAction
	public void setTableAttr(MgrTable tbc, Map<String, Object> model) {
		if (tbc == null) {
			return;
		}
		if(model==null) {
			return;
		}
		List fields = tbc.getFieldList();
		for (int i = 0; i < fields.size(); i++) {
			IField fld = (IField) fields.get(i);
			Object val = model.get(fld.getName());
			fld.putAttr("val", val);
		}
	}
	
	@NotAction
	public MgrTable getTable(String tbname, int type, String attrName) {
		if(StrUtil.isNotBlank(tbname)) {
			MgrTable tbc = getTableConfigService() !=null ? getTableConfigService().createTable(tbname, type) : null;
			setAttr(attrName, tbc);
			return tbc;
		}
		return null;
	}
	
	@NotAction
	public void setTable(String tbname, int type, String attrName) {
		getTable(tbname, type, attrName);
	}

	/**
	 * 将模型参数注入到Controller中
	 * @param model
	 */
	protected void setAttrs(Model<?> model) {
        String[] names = model._getAttrNames();
        for (String str : names) {
            setAttr(str, model.get(str));
        }
    }
	
	protected void setAttrs(Record model) {
        String[] names = model.getColumnNames();
        for (String str : names) {
            setAttr(str, model.get(str));
        }
    }
	
	@NotAction
	public BigInteger[] getParaValuesToBigInteger(String name) {
		String[] values = getRequest().getParameterValues(name);
		if (values == null) {
			return null;
		}
		BigInteger[] result = new BigInteger[values.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = new BigInteger(values[i]);
		}
		return result;
	}

	@NotAction
	public BigInteger getParaToBigInteger() {
		return toBigInteger(getPara(), null);
	}

	@NotAction
	public BigInteger getParaToBigInteger(int index) {
		return toBigInteger(getPara(index), null);
	}

	@NotAction
	public BigInteger getParaToBigInteger(int index, BigInteger defaultValue) {
		return toBigInteger(getPara(index), defaultValue);
	}

	@NotAction
	public BigInteger getParaToBigInteger(String name) {
		return toBigInteger(getRequest().getParameter(name), null);
	}

	@NotAction
	public BigInteger getParaToBigInteger(String name, BigInteger defaultValue) {
		return toBigInteger(getRequest().getParameter(name), defaultValue);
	}

	protected BigInteger toBigInteger(String value, BigInteger defaultValue) {
		try {
			if (value == null || "".equals(value.trim())) {
				return defaultValue;
			}
			value = value.trim();
			if (value.startsWith("N") || value.startsWith("n")) {
				return new BigInteger(value).negate();
			}
			return new BigInteger(value);
		} catch (Exception e) {
			throw new ActionException(404, "Can not parse the parameter \"" + value + "\" to BigInteger value.");
		}
	}
	
	protected BigInteger toBigInteger(Object value, BigInteger defaultValue) {
		if(value instanceof String) {
			return toBigInteger(value, defaultValue);
		}
		else if(value instanceof BigInteger) {
			return value==null ? defaultValue : (BigInteger) value;
		}
		else if(value instanceof Long) {
			return value==null ? defaultValue : BigInteger.valueOf((Long)value);
		}
		return toBigInteger(value.toString(), defaultValue);
	}
	
	@NotAction
	public Float getParaToFloat(int index) {
		return toFloat(getPara(index), null);
	}

	@NotAction
	public Float getParaToFloat(int index, Float defaultValue) {
		return toFloat(getPara(index), defaultValue);
	}

	@NotAction
	public Float getParaToFloat(String name) {
		return toFloat(getRequest().getParameter(name), null);
	}

	@NotAction
	public Float getParaToFloat(String name, Float defaultValue) {
		return toFloat(getRequest().getParameter(name), defaultValue);
	}
	
	private Float toFloat(String value, Float defaultValue) {
		try {
			if (StrUtil.isBlank(value)) {
				return defaultValue;
			}
			value = value.trim();
			if (value.startsWith("N") || value.startsWith("n")) {
				return -Float.parseFloat(value.substring(1));
			}
			return Float.parseFloat(value);
		}
		catch (Exception e) {
			throw new ActionException(400, "Can not parse the parameter \"" + value + "\" to Float value.");
		}
	}
	
	@NotAction
	public Double getParaToDouble(int index) {
		return toDouble(getPara(index), null);
	}

	@NotAction
	public Double getParaToDouble(int index, Double defaultValue) {
		return toDouble(getPara(index), defaultValue);
	}

	@NotAction
	public Double getParaToDouble(String name) {
		return toDouble(getRequest().getParameter(name), null);
	}

	@NotAction
	public Double getParaToDouble(String name, Double defaultValue) {
		return toDouble(getRequest().getParameter(name), defaultValue);
	}
	
	private Double toDouble(String value, Double defaultValue) {
		try {
			if (StrUtil.isBlank(value)) {
				return defaultValue;
			}
			value = value.trim();
			if (value.startsWith("N") || value.startsWith("n")) {
				return -Double.parseDouble(value.substring(1));
			}
			return Double.parseDouble(value);
		}
		catch (Exception e) {
			throw new ActionException(400, "Can not parse the parameter \"" + value + "\" to Double value.");
		}
	}
	
	/**
	 * 防止参数的sql注入  
	 * @param name
	 * @return
	 */
	@NotAction
	public String getParaTrans(String name) {
		String param  = this.getPara(name);
		if(param!=null && param.trim().length() > 0) {
			return SqlKit.transactSQLInjection(param);
		}
		return null;
	}
	
	@NotAction
	public String getParaTrans(String name, String defaultvalue) {
		String param  = this.getPara(name);
		if(param!=null && param.trim().length() > 0) {
			return SqlKit.transactSQLInjection(param);
		}
		return defaultvalue;
	}
	
	/**
	 * 获取对象数组
	 * @param modelClass
	 * @return
	 */
	@NotAction
	public <T> List<T> getModels(Class<T> modelClass) {
		return getModels(modelClass, StrUtil.lowerFirst(modelClass.getSimpleName()));
	}

	/**
	 * 获取前端传来的数组对象并响应成Model列表
	 */
	@NotAction
	public <T> List<T> getModels(Class<T> modelClass, String modelName) {
		List<String> indexes = getIndexes(modelName);
		List<T> list = new ArrayList<T>();
		for (String index : indexes) {
			T m = getModel(modelClass, modelName + "[" + index + "]");
			if (m != null) {
				list.add(m);
			}
		}
		return list;
	}
	
	/**
	 * 提取model对象数组的标号
	 */
	private List<String> getIndexes(String modelName) {
		// 提取标号
		List<String> list = new ArrayList<String>();
		String modelNameAndLeft = modelName + "[";
		Map<String, String[]> parasMap = getRequest().getParameterMap();
		for (Map.Entry<String, String[]> e : parasMap.entrySet()) {
			String paraKey = e.getKey();
			if (paraKey.startsWith(modelNameAndLeft)) {
				String no = paraKey.substring(paraKey.indexOf("[") + 1, paraKey.indexOf("]"));
				if (!list.contains(no)) {
					list.add(no);
				}
			}
		}
		return list;
	}
	
	@NotAction
	public boolean keepPara(String attr) {
		boolean kp = getParaToBoolean("kp", true);
		if(kp) {
			if(StrUtil.isNotBlank(attr)) {
				setAttr(attr, getParaMap());
			} else {
				keepPara();
			}
			return true;
		}
		return false;
	}

	@Override
	public void renderTemplate(String template) {
		super.renderTemplate(template);
		if(template.endsWith("html")) {
			//pv
			try {
				PageViewLogService pageViewLogService = Aop.get(PageViewLogService.class);
				if(pageViewLogService !=null) {
					pageViewLogService.log(this);
				}
			} catch (Exception e) {
				//未实现PageViewLogService，不处理
			}
		}
	}

	@Override
	public void render(String view) {
		super.render(view);
		if(view.endsWith("html")) {
			//pv
			try {
				PageViewLogService pageViewLogService = Aop.get(PageViewLogService.class);
				if(pageViewLogService !=null) {
					pageViewLogService.log(this);
				}
			} catch (Exception e) {
				//未实现PageViewLogService，不处理
			}
		}
	}

//	@NotAction
//	public void renderProxy(String targetName, String targetUri) {
//		render(GatewayRender.by(targetName, targetUri));
//	}
//
//	public void renderGateway(String targetName, String targetUri) {
//		render(GatewayRender.by(targetName, targetUri));
//	}
	
	@Override
	public void renderJson() {
		
		if(getAttr("REQUEST")!=null || getAttr("jp.menu")!=null) {
			removeAttr("REQUEST");
			removeAttr("i18n");
		}
		super.renderJson();
	}
	
	public void renderImage(String imageFileName) {
		render(new MimeTypeRender(imageFileName));
	}
	
	public void renderMineTypeFile(String fileName) {
		render(new MimeTypeRender(fileName));
	}

	/**
	 * 是否是手机浏览器
	 * 
	 * @return
	 */
	public boolean isMobileBrowser() {
		return ServletRequestKit.isMobileBrowser(getRequest());
	}

	/**
	 * 是否是微信浏览器
	 * 
	 * @return
	 */
	public boolean isWechatBrowser() {
		return ServletRequestKit.isWechatBrowser(getRequest());
	}

	/**
	 * 是否是IE浏览器
	 * 
	 * @return
	 */
	public boolean isIEBrowser() {
		return ServletRequestKit.isIEBrowser(getRequest());
	}

	/**
     * 是否是multpart的请求（带有文件上传的请求）
     *
     * @return
     */
    @NotAction
    public boolean isMultipartRequest() {
        return ServletRequestKit.isMultipartRequest(getRequest());
    }
	
    /**
     * 是否是ajax请求
     *
     * @return
     */
    @NotAction
    public boolean isAjaxRequest() {
        return ServletRequestKit.isAjaxRequest(getRequest());
    }
	
	@NotAction
	public String getSystemPath() {
		return "http://" + getRequest().getRemoteHost() + ":"
				+ getRequest().getLocalPort() + getRequest().getContextPath();
	}
	
	protected boolean isPOST() {
		return getRequest().getMethod().equals("POST");
	}
	
	protected boolean isGET() {
		return getRequest().getMethod().equals("GET");
	}
	
	protected boolean isPut() {
		return getRequest().getMethod().equals("PUT");
	}
	
	protected boolean isDelete() {
		return getRequest().getMethod().equals("DELETE");
	}
	
	protected boolean isMethod(String methodName) {
		return getRequest().getMethod().equals(methodName);
	}
	
	protected String ctx() {
		return getRequest().getContextPath();
	}
	
	protected String ctxd() {
		String ctx = getRequest().getContextPath();
		ctx = StrUtil.isNotBlank(ctx) ? ctx.substring(1) + "/" : ctx;
		return ctx;
	}

	public String getResultType() {
		return getPara("resultType", "default");
	}
}
